/**
 * Filter by a configurable Ext.picker.DatePicker menu
 * 
 * Example Usage:
 * 
 * var filters = Ext.create('Ext.ux.grid.GridFilters', { ... filters: [{ //
 * required configs type: 'date', dataIndex: 'dateAdded',
 *  // optional configs dateFormat: 'm/d/Y', // default beforeText: 'Before', //
 * default afterText: 'After', // default onText: 'On', // default pickerOpts: { //
 * any DatePicker configs },
 * 
 * active: true // default is false }] });
 */
Ext.define('Ext.ux.grid.filter.DateFilter', {
	extend : 'Ext.ux.grid.filter.Filter',
	alias : 'gridfilter.date',
	uses : ['Ext.picker.Date', 'Ext.menu.Menu'],

	/**
	 * @cfg {String} afterText Defaults to 'After'.
	 */
	afterText : 'After',
	/**
	 * @cfg {String} beforeText Defaults to 'Before'.
	 */
	beforeText : 'Before',
	/**
	 * @cfg {Object} compareMap Map for assigning the comparison values used in
	 *      serialization.
	 */
	compareMap : {
		before : 'lt',
		after : 'gt',
		on : 'eq'
	},
	/**
	 * @cfg {String} dateFormat The date format to return when using getValue.
	 *      Defaults to 'm/d/Y'.
	 */
	dateFormat : 'm/d/Y',

	/**
	 * @cfg {Date} maxDate Allowable date as passed to the Ext.DatePicker
	 *      Defaults to undefined.
	 */
	/**
	 * @cfg {Date} minDate Allowable date as passed to the Ext.DatePicker
	 *      Defaults to undefined.
	 */
	/**
	 * @cfg {Array} menuItems The items to be shown in this menu Defaults to:
	 * 
	 * <pre>
	 * menuItems : ['before', 'after', '-', 'on'],
	 * </pre>
	 */
	menuItems : ['before', 'after', '-', 'on'],

	/**
	 * @cfg {Object} menuItemCfgs Default configuration options for each menu
	 *      item
	 */
	menuItemCfgs : {
		selectOnFocus : true,
		width : 125
	},

	/**
	 * @cfg {String} onText Defaults to 'On'.
	 */
	onText : 'On',

	/**
	 * @cfg {Object} pickerOpts Configuration options for the date picker
	 *      associated with each field.
	 */
	pickerOpts : {},

	/**
	 * @private Template method that is to initialize the filter and install
	 *          required menu items.
	 */
	init : function(config) {
		var me = this, pickerCfg, i, len, item, cfg;

		pickerCfg = Ext.apply(me.pickerOpts, {
					xtype : 'datepicker',
					minDate : me.minDate,
					maxDate : me.maxDate,
					format : me.dateFormat,
					listeners : {
						scope : me,
						select : me.onMenuSelect
					}
				});

		me.fields = {};
		for (i = 0, len = me.menuItems.length; i < len; i++) {
			item = me.menuItems[i];
			if (item !== '-') {
				cfg = {
					itemId : 'range-' + item,
					text : me[item + 'Text'],
					menu : Ext.create('Ext.menu.Menu', {
								plain : true,
								items : [Ext.apply(pickerCfg, {
											itemId : item,
											listeners : {
												select : me.onPickerSelect,
												scope : me
											}
										})]
							}),
					listeners : {
						scope : me,
						checkchange : me.onCheckChange
					}
				};
				item = me.fields[item] = Ext.create('Ext.menu.CheckItem', cfg);
			}
			// me.add(item);
			me.menu.add(item);
		}
		me.values = {};
	},

	onCheckChange : function(item, checked) {
		var me = this, picker = item.menu.items.first(), itemId = picker.itemId, values = me.values;

		if (checked) {
			values[itemId] = picker.getValue();
		} else {
			delete values[itemId];
		}
		me.setActive(me.isActivatable());
		me.fireEvent('update', me);
	},

	/**
	 * @private Handler method called when there is a keyup event on an input
	 *          item of this menu.
	 */
	onInputKeyUp : function(field, e) {
		var k = e.getKey();
		if (k == e.RETURN && field.isValid()) {
			e.stopEvent();
			this.menu.hide();
		}
	},

	/**
	 * Handler for when the DatePicker for a field fires the 'select' event
	 * 
	 * @param {Ext.picker.Date}
	 *            picker
	 * @param {Object}
	 *            date
	 */
	onMenuSelect : function(picker, date) {
		var fields = this.fields, field = this.fields[picker.itemId];

		field.setChecked(true);

		if (field == fields.on) {
			fields.before.setChecked(false, true);
			fields.after.setChecked(false, true);
		} else {
			fields.on.setChecked(false, true);
			if (field == fields.after && this.getFieldValue('before') < date) {
				fields.before.setChecked(false, true);
			} else if (field == fields.before
					&& this.getFieldValue('after') > date) {
				fields.after.setChecked(false, true);
			}
		}
		this.fireEvent('update', this);

		picker.up('menu').hide();
	},

	/**
	 * @private Template method that is to get and return the value of the
	 *          filter.
	 * @return {String} The value of this filter
	 */
	getValue : function() {
		var key, result = {};
		for (key in this.fields) {
			if (this.fields[key].checked) {
				result[key] = this.getFieldValue(key);
			}
		}
		return result;
	},

	/**
	 * @private Template method that is to set the value of the filter.
	 * @param {Object}
	 *            value The value to set the filter
	 * @param {Boolean}
	 *            preserve true to preserve the checked status of the other
	 *            fields. Defaults to false, unchecking the other fields
	 */
	setValue : function(value, preserve) {
		var key;
		for (key in this.fields) {
			if (value[key]) {
				this.getPicker(key).setValue(value[key]);
				this.fields[key].setChecked(true);
			} else if (!preserve) {
				this.fields[key].setChecked(false);
			}
		}
		this.fireEvent('update', this);
	},

	/**
	 * Template method that is to return <tt>true</tt> if the filter has
	 * enough configuration information to be activated.
	 * 
	 * @return {Boolean}
	 */
	isActivatable : function() {
		var key;
		for (key in this.fields) {
			if (this.fields[key].checked) {
				return true;
			}
		}
		return false;
	},

	/**
	 * @private Template method that is to get and return serialized filter data
	 *          for transmission to the server.
	 * @return {Object/Array} An object or collection of objects containing key
	 *         value pairs representing the current configuration of the filter.
	 */
	getSerialArgs : function() {
		var args = [];
		for (var key in this.fields) {
			if (this.fields[key].checked) {
				args.push({
							type : 'date',
							comparison : this.compareMap[key],
							value : Ext.Date.format(this.getFieldValue(key),
									this.dateFormat)
						});
			}
		}
		return args;
	},

	/**
	 * Get and return the date menu picker value
	 * 
	 * @param {String}
	 *            item The field identifier ('before', 'after', 'on')
	 * @return {Date} Gets the current selected value of the date field
	 */
	getFieldValue : function(item) {
		return this.values[item];
	},

	/**
	 * Gets the menu picker associated with the passed field
	 * 
	 * @param {String}
	 *            item The field identifier ('before', 'after', 'on')
	 * @return {Object} The menu picker
	 */
	getPicker : function(item) {
		return this.fields[item].menu.items.first();
	},

	/**
	 * Template method that is to validate the provided Ext.data.Record against
	 * the filters configuration.
	 * 
	 * @param {Ext.data.Record}
	 *            record The record to validate
	 * @return {Boolean} true if the record is valid within the bounds of the
	 *         filter, false otherwise.
	 */
	validateRecord : function(record) {
		var key, pickerValue, val = record.get(this.dataIndex), clearTime = Ext.Date.clearTime;

		if (!Ext.isDate(val)) {
			return false;
		}
		val = clearTime(val, true).getTime();

		for (key in this.fields) {
			if (this.fields[key].checked) {
				pickerValue = clearTime(this.getFieldValue(key), true)
						.getTime();
				if (key == 'before' && pickerValue <= val) {
					return false;
				}
				if (key == 'after' && pickerValue >= val) {
					return false;
				}
				if (key == 'on' && pickerValue != val) {
					return false;
				}
			}
		}
		return true;
	},

	onPickerSelect : function(picker, date) {
		// keep track of the picker value separately because the menu gets
		// destroyed
		// when columns order changes. We return this value from getValue()
		// instead
		// of picker.getValue()
		this.values[picker.itemId] = date;
		this.fireEvent('update', this);
	}
});
